package com.jackrain.nea.shiro.stateful;

import com.jackrain.nea.util.RedisOpsUtil;
import com.jackrain.nea.util.Validator;
import com.jackrain.nea.web.face.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Created by Lucky dog on 2016/7/19.
 */
@Slf4j
public class CachingShiroSessionDao  extends CachingSessionDAO{


    @Value("${raincloud.session.max-active}")
    private int maxSession;


    @Autowired
    private RedisOpsUtil<String,String> redisOpsUtil;

    //@Autowired
    //private LoginInfoReposity loginInfoReposity;
    /**
     * 重写CachingSessionDAO中readSession方法，如果Session中没有登陆信息就调用doReadSession方法从Redis中重读
     */
    @Override
    public Session readSession(Serializable sessionId) throws UnknownSessionException {
        Session session = this.doReadSession(sessionId);
        if (session == null) {
            log.debug("There is no session with id [" + sessionId + "]");
        }
        return session;
    }

    /**
     * 根据会话ID获取会话
     *
     * @param sessionId 会话ID
     * @return ShiroSession
     */
    @Override
    protected Session doReadSession(Serializable sessionId) {
        Session session = null;
        try {
            session =  (Session) redisOpsUtil.objRedisTemplate.opsForHash().get("session",sessionId);
            log.debug("sessionId "+sessionId+ " 被读取");
            return session;

        } catch (Exception e) {
            log.debug("读取Session失败："+e.getMessage(), e);
        }
        return session;
    }

    /**
     * 如DefaultSessionManager在创建完session后会调用该方法；
     * 如保存到关系数据库/文件系统/NoSQL数据库；即可以实现会话的持久化；
     * 返回会话ID；主要此处返回的ID.equals(session.getId())；
     */
    @Override
    protected Serializable doCreate(Session session) {
        // 创建一个Id并设置给Session
        Serializable sessionId = this.generateSessionId(session);
        assignSessionId(session,sessionId);
        try {
            redisOpsUtil.objRedisTemplate.opsForHash().put("session",sessionId,session);
            User user = (User) session.getAttribute("user");
            //saveCurrentLoginInfo(user.getName(),sessionId.toString());
            log.debug("创建Session成功："+session.getId());
        } catch (Exception e) {
            log.debug("创建Session失败："+e.getMessage(), e);
        }
        return sessionId;
    }

//    private void saveCurrentLoginInfo(String userName,String sessionId) {
//        HttpServletRequest request=((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        String ip = WebUtils.getIpAddress(request);
//        Date loginTime = new Date();
//        loginInfoReposity.putLoginInfo(sessionId,userName, ip,loginTime);
//    }



    /**
     * 更新会话；如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
     */
    @Override
    protected void doUpdate(Session session) {
        try {
            String sessionKey = "session";
            Object o = session.getAttribute("user");
            log.debug("o ->"+o);
            SimpleSession origSession = (SimpleSession) redisOpsUtil.objRedisTemplate.opsForHash().get(sessionKey, session.getId());
            redisOpsUtil.objRedisTemplate.opsForHash().put(sessionKey, session.getId(), session);
            log.debug("更新Session成功"+session.getId());
            if(Validator.isNotNull(o) && origSession.getAttribute("user") == null){
                User user = (User) o;
                String userKey = "user:" + user.getName();
                String clientKey = "client:" + user.getClientId();
                String usercntKey = "usercnt:"+user.getName();

                log.debug("before ->doUpdate");
                redisOpsUtil.strRedisTemplate.execute(new SessionCallback<Object>(){
                    @Override
                    public List<Object> execute(RedisOperations operations) {
                        operations.multi();
                        operations.opsForList().leftPush(userKey, session.getId().toString());
                        redisOpsUtil.strRedisTemplate.opsForList().remove(clientKey, 0, userKey);
                        operations.opsForList().leftPush(clientKey,userKey);
                        redisOpsUtil.strRedisTemplate.opsForValue().increment(usercntKey,1);
                        log.debug("user client usercnt update"+session.getId());
                        return operations.exec();
                    }
                });
                log.debug("after ->doUpdate");
                //kickout last session when session num > maxsession
                int sessionNum = Integer.parseInt(redisOpsUtil.strRedisTemplate.opsForValue().get(usercntKey).toString());
                if (sessionNum >= maxSession+1){
                    String sessionId = redisOpsUtil.strRedisTemplate.opsForList().index(userKey,1);
                    Session lastSession = (Session)redisOpsUtil.objRedisTemplate.opsForHash().get("session",sessionId);
                    onDelete(lastSession);
                    redisOpsUtil.strRedisTemplate.opsForSet().add("outsession",lastSession.getId().toString());
                    log.debug("outsession ->"+lastSession.getId().toString());
                }

                log.debug("login ->"+user.getName()+":"+user.getLastloginip()+":"+user.getLastlogindate());
            }
        } catch (Exception e) {
            log.debug("更新Session失败："+e.getMessage(), e);
        }
    }

    /**
     * 删除会话；当会话过期/会话停止（如用户退出时）会调用
     **/
    protected void onDelete(Session session) {
        try {
            redisOpsUtil.objRedisTemplate.opsForHash().delete("session",session.getId().toString());
            //loginInfoReposity.deleteBySessionId(session.getId().toString());
            User user = (User) session.getAttribute("user");
            if(user != null) {
                String userKey = "user:" + user.getName();
                String clientKey = "client:" + user.getClientId();
                String usercntKey = "usercnt:"+user.getName();
                Long index = redisOpsUtil.strRedisTemplate.opsForList().remove(userKey, 1, session.getId().toString());
                if(index > 0){
                    Long count = redisOpsUtil.objRedisTemplate.opsForValue().increment(usercntKey,-1);
                    if(count <= 0){
                        redisOpsUtil.objRedisTemplate.delete(usercntKey);
                        redisOpsUtil.strRedisTemplate.delete(userKey);
                        redisOpsUtil.strRedisTemplate.opsForList().remove(clientKey, 0, userKey);
                    }
                }
                log.debug("logout ->"+user.getName()+":"+user.getLastloginip()+":"+user.getLastlogindate());
            }
            log.debug("删除Session成功");
        }catch (Exception e){
            log.debug("删除Session失败："+e.getMessage(),e);
        }
    }



    @Override
    protected void doDelete(Session session) {
        onDelete(session);
    }


    /**
     * 删除cache中缓存的Session
     **/
    public void uncache(Serializable sessionId) {}

    /**
     * 获取当前所有活跃用户，如果用户量多此方法影响性能
     **/
    @Override
    public Collection<Session> getActiveSessions() {
        List<Session> sessionList = new ArrayList<Session>();
        try{
            List<Object> ls = redisOpsUtil.objRedisTemplate.opsForHash().values("session");
            for (Object o : ls) {
                Session session = (Session) o;
                sessionList.add(session);
            }
        }catch (Exception e){
            log.error("Validating session fail:"+e.getMessage(),e);
        }
        return sessionList;
    }


}
